﻿using System;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net;

namespace Server.Packets
{
    /// <summary>
    /// Represents a connection packet, containing mandatory information in order to connect to a server.
    /// </summary>
    internal class S0ConnectionPacket : Packet
    {
        #region Fields

        /// <summary>
        /// The <see cref="T:Server.ConnectionInformations" />.
        /// </summary>
        private ConnectionInformations m_connectionInformations;

        /// <summary>
        /// Client address.
        /// </summary>
        private string m_clientAddress;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="T:Server.Packets.S0ConnectionPacket" /> class.
        /// </summary>
        public S0ConnectionPacket()
            : this(new ConnectionInformations(Constants.AddressDefaultValue, IPEndPoint.MinPort, Constants.ClientVersionDefaultValue, Constants.UpdateAddressDefaultValue), Constants.AddressDefaultValue)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="T:Server.Packets.S0ConnectionPacket" /> class with a given address and port.
        /// </summary>
        /// <param name="connectionInformations">The <see cref="T:Server.ConnectionInformations" />.</param>
        /// <param name="clientAddress">The client address.</param>
        internal S0ConnectionPacket(ConnectionInformations connectionInformations, string clientAddress)
        {
            Contract.Requires<ArgumentNullException>(!String.IsNullOrWhiteSpace(clientAddress));

            m_connectionInformations = connectionInformations;
            m_clientAddress = clientAddress;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Identifier.
        /// </summary>
        protected override byte Id
        {
            get { return 0; }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Define the invariant valid state for this class.
        /// </summary>
        [ContractInvariantMethod]
        private void InvariantValidState()
        {
            Contract.Invariant(!String.IsNullOrWhiteSpace(m_clientAddress));
        }

        /// <summary>
        /// Converts this instance to its equivalent string representation.
        /// </summary>
        /// <returns>The string representation of the value of this instance.</returns>
        public override string ToString()
        {
            return String.Format("S0ConnectionPacket - {0} ; client address : {1}", m_connectionInformations.ToString(), m_clientAddress);
        }

        /// <summary>
        /// Reads the raw packet data from the data reader.
        /// </summary>
        /// <param name="reader">Data reader.</param>
        /// <exception cref="T:System.IO.EndOfStreamException">The end of the stream is reached.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The stream is closed.</exception>
        /// <exception cref="T:System.IO.IOException">An I/O error occurs.</exception>
        /// <exception cref="T:System.IO.InvalidDataException">The server address read was null or empty, or if value consists exclusively of white-space characters -or- the port read was lower than the minimum value or greater than the maximum value that can be assigned to a port -or- the client version read was null or empty, or if value consists exclusively of white-space characters -or- the update address read was null or empty, or if value consists exclusively of white-space characters -or- the client address read was null or empty, or if value consists exclusively of white-space characters -or- The port read was lower than the minimum value or greater than the maximum value that can be assigned to a port.</exception>
        protected override void ReadPacketData(BinaryReader reader)
        {
            Contract.Ensures(!String.IsNullOrWhiteSpace(m_clientAddress));

            m_connectionInformations = new ConnectionInformations(reader);

            var clientAddress = reader.ReadString();
            if (String.IsNullOrWhiteSpace(clientAddress))
                throw new InvalidDataException("The client address read was null or empty, or if value consists exclusively of white-space characters.");
            m_clientAddress = clientAddress;
        }

        /// <summary>
        /// Write the raw packet data to the data writer.
        /// </summary>
        /// <param name="writer">Data writer.</param>
        /// <exception cref="T:System.IO.IOException">An I/O error occurs.</exception>
        /// <exception cref="T:System.ObjectDisposedException">The stream is closed.</exception>
        protected override void WritePacketData(BinaryWriter writer)
        {
            Contract.Assert(!String.IsNullOrWhiteSpace(m_clientAddress));

            m_connectionInformations.Write(writer);
            writer.Write(m_clientAddress);
        }

        #endregion
    }
}
